package nl.ejvos.bgpgames.tictactoe.server;

import nl.ejvos.bgpgames.tictactoe.shared.ClickedMessage;
import nl.ejvos.bgpgames.tictactoe.shared.Constants;
import nl.ejvos.bgpgames.tictactoe.shared.EntireStateMessage;
import nl.ejvos.bgpgames.tictactoe.shared.RestartMessage;

import com.google.gwt.user.client.Timer;
import com.hu.bgp.framework.GameContext;
import com.hu.bgp.framework.GameState;
import com.hu.bgp.framework.Server;
import com.hu.bgp.framework.clientapi.ClientToServerMessage;
import com.hu.bgp.server.Player;
import com.hu.bgp.shared.OverviewUpdateMessage;

/**
 * Game logica van boter, kaas & eieren.
 * 
 * @author ejvos
 */
public class TicTacToeServer extends Server
{
	private int gameStatus;
	private int[][] tileStatus =
	{
		{
			Constants.tsAny, Constants.tsAny, Constants.tsAny
		},
		{
			Constants.tsAny, Constants.tsAny, Constants.tsAny
		},
		{
			Constants.tsAny, Constants.tsAny, Constants.tsAny
		}
	};
	private Player crossPlayer;
	private Player noughtPlayer;

	public TicTacToeServer(GameContext gameContext)
	{
		super(gameContext);
		gameContext.changeState(GameState.WAITING_FOR_PLAYERS);
	}

	private void doTurn()
	{
		if (gameStatus == Constants.gsCrossPlaying || gameStatus == Constants.gsNoughtPlaying)
		{
			// check for a winner
			boolean winner = false;

			for (int k = 0; k < 8; k++)
			{
				boolean success = true;
				boolean found = false;

				for (int i = 0; i < 3; i++)
				{
					success = success && (tileStatus[Constants.rowWins[k][i]][Constants.colWins[k][i]] == (gameStatus == Constants.gsNoughtPlaying ? Constants.tsNought : Constants.tsCross));
					found = found || (tileStatus[Constants.rowWins[k][i]][Constants.colWins[k][i]] == (gameStatus == Constants.gsNoughtPlaying ? Constants.tsNought : Constants.tsCross));
				}

				if (success && found)
				{
					// somebody won
					winner = true;
					break;
				}
			}
			if (winner)
			{
				gameStatus = gameStatus == Constants.gsCrossPlaying ? Constants.gsCrossWon : Constants.gsNoughtWon;
				updateWinner();
			}
			else
			{
				// check for draw
				boolean emptyFound = false;
				for (int j = 0; j < 3; j++)
					for (int k = 0; k < 3; k++)
						if (tileStatus[j][k] == Constants.tsAny)
							emptyFound = true;

				if (!emptyFound)
				{
					gameStatus = Constants.gsNobodyWon;
				}
				else
				{
					// nobody won and still tiles open, keep the game going
					if (gameStatus == Constants.gsCrossPlaying)
					{
						gameStatus = Constants.gsNoughtPlaying;
					}
					else if (gameStatus == Constants.gsNoughtPlaying)
					{
						gameStatus = Constants.gsCrossPlaying;
					}
				}
			}
		}
	}

	public void clickTile(Player player, int x, int y)
	{
		if (tileStatus[x][y] == Constants.tsAny)
		{
			if (gameStatus == Constants.gsCrossPlaying && player == crossPlayer)
			{
				tileStatus[x][y] = Constants.tsCross;
				doTurn();
			}
			else if (gameStatus == Constants.gsNoughtPlaying && player == noughtPlayer)
			{
				tileStatus[x][y] = Constants.tsNought;
				doTurn();
			}
			updateAllClients();
		}
	}

	public void start()
	{
		gameStatus = Constants.gsCrossPlaying;
		for (int j = 0; j < 3; j++)
		{
			for (int k = 0; k < 3; k++)
			{
				tileStatus[j][k] = Constants.tsAny;
			}
		}
		updateAllClients();
		updateOverview();
	}

	public void handleMessage(ClientToServerMessage message, Player fromPlayer)
	{
		if (message instanceof ClickedMessage)
		{
			ClickedMessage clickedMessage = (ClickedMessage) message;
			clickTile(fromPlayer, clickedMessage.getX(), clickedMessage.getY());
		}
		else if (message instanceof RestartMessage)
		{
			// restart();
		}
	}

	private void updateAllClients()
	{
		EntireStateMessage message = new EntireStateMessage(gameStatus, tileStatus);
		broadcastMessage(message);
	}

	private void updateOverview()
	{
		StringBuilder overview = new StringBuilder();
		for (Player p : gameContext.getPlayers())
		{
			overview.append("player: " + p.getProfile().getName() + "\n");
		}
		OverviewUpdateMessage message = new OverviewUpdateMessage(overview.toString());
		broadcastMessage(message);
	}

	public void updateWinner()
	{
		Player pWinner = gameStatus == Constants.gsCrossWon ? crossPlayer : noughtPlayer;
		Player pLoser = gameStatus == Constants.gsCrossWon ? noughtPlayer : crossPlayer;
		pWinner.addScore("You won!");
		pLoser.addScore("You lost...");
		gameContext.changeState(GameState.ENDED);
	}

	public void playerJoined(Player player)
	{
		if (gameContext.getState() == GameState.WAITING_FOR_PLAYERS)
		{
			if (gameContext.getPlayers().size() == 2)
			{
				crossPlayer = gameContext.getInitialPlayer();
				noughtPlayer = gameContext.getPlayers().get(1);
				gameContext.changeState(GameState.RUNNING);
				broadcastMinimumPlayersReachedEvent();
			}
		}
	}

	public void playerLeft(Player player)
	{
		updateOverview();
	}

	public void playerRejoined(Player player)
	{
		Timer timer = new Timer()
		{
			public void run()
			{
				updateAllClients();
				updateOverview();
			}
		};
		timer.schedule(1000);
	}
}
